package src.TestSuite;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;

import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import src.MyBinarySearch;
import src.MyDynamicStack;
import src.MyGenericsStack;
import src.InfixFullParantEx;
import src.MyLinearSearch;
import src.MyMergeSort;
import src.RecursiveSearch;
import src.TowersOfHanoiImpl;

public class TestSuite {
	private final ByteArrayOutputStream errConsole = new ByteArrayOutputStream();
	private final ByteArrayOutputStream outConsole = new ByteArrayOutputStream();
	
	/**
	 * Before/After methods
	 */
	@Before
	public void setErrStream() {
	    System.setErr(new PrintStream(errConsole));
	}
	@After
	public void cleanErrStream() {
	    System.setErr(null);
	}
	
	@Before
	public void setOutStream() {
		System.setOut(new PrintStream(outConsole,false));
	}
	@After
	public void cleanOutStream() {
		System.setOut(null);
	}
	
	/**
	 * MyGenericsStack.class
	 */
	private MyGenericsStack<String> myGS = new MyGenericsStack<String>(5);
	
	@Test
	public void testMyGenericsStackObject() {
		assertNotNull(myGS);
	}
	
	public void testMyGenericsStackPush() {
		assertNotNull(myGS);
		assertFalse(myGS.isStackFull());
		while(!myGS.isStackFull()) {
			myGS.push("a");
		}
		assertTrue(myGS.isStackFull());
		myGS.push("b");
		assertFalse(myGS.isStackFull());
		testMyGenericsStackPeek();
	} 
	
	@Test
	public void testMyGenericsStackPushPop() {
		testMyGenericsStackPush();
		try {
			while(!myGS.isStackEmpty()) {
				myGS.pop();
			}
			myGS.pop();
		} catch(Exception ex) {
			assertEquals("Stack is empty. Can not remove element.",ex.getMessage());
		}
	}
	
	public void testMyGenericsStackPeek() {
		assertEquals(myGS.peek(), "b");
	}
	
	/**
	 * InfixFullParantEx.class
	 */    
	@Test
	public void testEvaluateInfix() {
		/* operations */
		String sum = InfixFullParantEx.evaluateInfix("{10+ 2}");
		assertEquals("12",sum);
		String mult = InfixFullParantEx.evaluateInfix("(5*3)");
		assertEquals("15",mult);
		String sub = InfixFullParantEx.evaluateInfix("{10 -8}");
		assertEquals("2",sub);
		String div = InfixFullParantEx.evaluateInfix("(30 / 3)");
		assertEquals("10",div);
		/* no start char */
		String res = InfixFullParantEx.evaluateInfix("3+5)");
		assertEquals("8",res);
		/* invalid end char */
		res = InfixFullParantEx.evaluateInfix("(3 / 3+]");
		assertEquals("+",res);
		/* invalid operator */
		res = InfixFullParantEx.evaluateInfix("(3 ( 3)");
		assertEquals("0",res);
		/* divide by zero */
		@SuppressWarnings("unused")
		String divzero = InfixFullParantEx.evaluateInfix("{10/ 0} ");
		assertTrue(errConsole.toString().startsWith("java.lang.ArithmeticException: / by zero"));
	}
	
	@Test
	public void testInfixFullParantEx() {
		InfixFullParantEx infixEvaluate = new InfixFullParantEx();
		assertNotNull(infixEvaluate);
	}
	
	/**
	 * MyDynamicStack.class
	 */
	private int size = 5;
	private int entry = 1;
	private MyDynamicStack myDS = new MyDynamicStack(size);
	
	@Test
	public void testMyDynamicStack() {
		assertNotNull(myDS);
	}
	
	public void testMyDynamicStackPush() {
		for(int i=0; i<size; i++) {
			myDS.push(entry++);
		}
		assertTrue(myDS.isStackFull());
		myDS.push(entry++);
		String[] myDynamicStackOutput = outConsole.toString().split("\r\n");
		assertEquals("Stack is full. Increasing the capacity.", myDynamicStackOutput[myDynamicStackOutput.length-1]);
		assertFalse(myDS.isStackFull());
		testMyDynamicStackPeek();		
	}

	@Test
	public void testMyDynamicStackPushPop() {
		testMyDynamicStackPush();
		try {
			while(!myDS.isStackEmpty()) {
				myDS.pop();
			}
			myDS.pop();
		} catch(Exception ex) {
			assertEquals("Stack is empty. Can not remove element.",ex.getMessage());
		}
	}

	public void testMyDynamicStackPeek() {
		long peek = myDS.peek();
		assertEquals(6,peek);
	}
	
	/**
	 * TowersOfHanoiImpl.class
	 */
	private String[] hanoiExpected = {
			"Move disk 1 from tower 1 to top of tower 3",
			"Move disk 2 from tower 1 to top of tower 2",
			"Move disk 1 from tower 3 to top of tower 2",
			"Move disk 3 from tower 1 to top of tower 3",
			"Move disk 1 from tower 2 to top of tower 1",
			"Move disk 2 from tower 2 to top of tower 3",
			"Move disk 1 from tower 1 to top of tower 3",
			"Move disk 4 from tower 1 to top of tower 2",
			"Move disk 1 from tower 3 to top of tower 2",
			"Move disk 2 from tower 3 to top of tower 1",
			"Move disk 1 from tower 2 to top of tower 1",
			"Move disk 3 from tower 3 to top of tower 2",
			"Move disk 1 from tower 1 to top of tower 3",
			"Move disk 2 from tower 1 to top of tower 2",
			"Move disk 1 from tower 3 to top of tower 2"
	};
		
	@Test
	public void testInstantiateObject() {
		TowersOfHanoiImpl towersHanoi = new TowersOfHanoiImpl();
		assertNotNull(towersHanoi);
	}
	
	@Test
	public void testTowersOfHanoi() {
		TowersOfHanoiImpl.towersOfHanoi(4);
		String[] hanoiOutput = outConsole.toString().split("\r\n");
		assertArrayEquals(hanoiExpected,hanoiOutput);
		TowersOfHanoiImpl.showTowerStates(4, 1, 2, 3);
	}
	
	/**
	 * MyMergeSort.class
	 */
	@Test
	public void testMyMergeSort() {
		MyMergeSort.main(new String[] {});
		assertEquals("4 11 23 28 43 45 65 77 89 98 ", outConsole.toString());
	}
	
	/**
	 * MyLinearSearch.class
	 */
	private int[] inputArrayMyLS = {1, 2, 3, 4};
	private int keyFoundMyLS = 4;
	private int keyNotFoundMyLS = 6;
	
	@Test
	public void testMyLinearSearch() {
		MyLinearSearch MyLS = new MyLinearSearch();
		assertNotNull(MyLS);
	}
	
	@Test
	public void testLinerSearchFind() {
		int index = MyLinearSearch.linerSearch(inputArrayMyLS, keyFoundMyLS);
		assertTrue(index>0);
	}
		
	@Test
	public void testLinerSearchFail() {
		int index = MyLinearSearch.linerSearch(inputArrayMyLS, keyNotFoundMyLS);
		assertTrue(index<0);
	}
	
	/**
	 * MyBinarySearch.class
	 */
	private MyBinarySearch myBS = new MyBinarySearch();
	private int[] inputArrayMyBS = { 2,6,8,10,12,14,16 };
	private int keyFoundMyBS = 8;
	private int keyNotFoundMyBS = 7;
	
	@Test
	public void testBinarySearchFind() {
		assertNotNull(myBS);
		int index = myBS.binarySearch(inputArrayMyBS,/*key*/keyFoundMyBS);
		assertTrue(index>0);
	}
	
	@Test
	public void testBinarySearchFail() {
		assertNotNull(myBS);
		int index = myBS.binarySearch(inputArrayMyBS,/*key*/keyNotFoundMyBS);
		assertTrue(index<0);
	}
	
	/**
	 * RecursiveSearch.class
	 */
	public int[] inputArrayRS = { 2,6,8,10,12,14,16 };
	public int keyFoundRS = 8;
	public int keyNotFoundRS = 7;
	
	@Test
	public void testRecursiveSearch() {
		RecursiveSearch recursiveSearch = new RecursiveSearch();
		assertNotNull(recursiveSearch);
	}
	
	@Test
	public void testRecursiveSearchFind() {	
		int index = RecursiveSearch.recursiveSearch(inputArrayRS,/*start*/0,/*end*/6,/*key*/keyFoundRS);
		assertTrue(index>0);
	}
	
	@Test
	public void testRecursiveSearchFail() {
		int index = RecursiveSearch.recursiveSearch(inputArrayRS,/*start*/0,/*end*/6,/*key*/keyNotFoundRS);
		assertTrue(index<0);
	}
}
